
#include "MyGame.h"
#include <functional>
#include <RCRectangle.h>


#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>

MyGame::MyGame(void)
{
	setTitle(L"RC Sand Box 2D");
	
	rotation = 0;
	rotation2 = 0;

	bg = 0;
	l1 = 0;
	l2 = 0;

	playerPos = XMFLOAT2(200, 200);

	for (int m = 0; m<5; m++)
		mines[m] = XMFLOAT2(200 * (m + 1), (m + 1) * 60);

	for (int l = 0; l<4; l++)
	{
		activeLaser[l] = false;
		lasers[l] = XMFLOAT2(900, 0);
	}
}


MyGame::~MyGame(void)
{
	pMineTexture->Release();
	pShipTexture->Release();

	spriteBatch = nullptr;

	mainBG->Release();
	layer1->Release();
	layer2->Release();

	laser->Release();
}

void MyGame::Update(float time, std::vector<IUpdateable*>* updateables)
{
	HandleKeyboardInput();

	RCGame::Update(time, updateables);
}

void MyGame::HandleKeyboardInput()
{
	float spd = .05f;
	float rotSpd = .01f;

	float playerSpeed = 8.0f;

	if (KeyboardManager.getState()[DIK_ESCAPE] / 128)
		RCGame::Stop();

	if (KeyboardManager.getState()[DIK_LEFT] / 128)
		playerPos.x -= playerSpeed;
	
	if (KeyboardManager.getState()[DIK_RIGHT] / 128)
		playerPos.x += playerSpeed;
	
	if (KeyboardManager.getState()[DIK_UP] / 128)
		playerPos.y -= playerSpeed;
	
	if (KeyboardManager.getState()[DIK_DOWN] / 128)
		playerPos.y += playerSpeed;
	
	if (playerPos.x < 0)
		playerPos.x = 0;
	if (playerPos.x > GraphicsDevice.getViewPort().Width)
		playerPos.x = GraphicsDevice.getViewPort().Width;

	if (playerPos.y < 0)
		playerPos.y = 0;
	if (playerPos.y > GraphicsDevice.getViewPort().Height)
		playerPos.y = GraphicsDevice.getViewPort().Height;	
}

void MyGame::Initialize()
{
	RCGame::Initialize();

	laserSound.LoadAsset(L"sound01.wav");
	bangSound.LoadAsset(L"explosion.wav");
	
	D3DX11CreateShaderResourceViewFromFile(getGraphicsDevice()->getDevice(), L"mineAnimation.png", NULL, NULL, &pMineTexture, NULL);
	D3DX11CreateShaderResourceViewFromFile(getGraphicsDevice()->getDevice(), L"shipAnimation.png", NULL, NULL, &pShipTexture, NULL);

	D3DX11CreateShaderResourceViewFromFile(getGraphicsDevice()->getDevice(), L"mainbackground.png", NULL, NULL, &mainBG, NULL);
	D3DX11CreateShaderResourceViewFromFile(getGraphicsDevice()->getDevice(), L"bgLayer1.png", NULL, NULL, &layer1, NULL);
	D3DX11CreateShaderResourceViewFromFile(getGraphicsDevice()->getDevice(), L"bgLayer2.png", NULL, NULL, &layer2, NULL);
	D3DX11CreateShaderResourceViewFromFile(getGraphicsDevice()->getDevice(), L"laser.png", NULL, NULL, &laser, NULL);

	D3DX11CreateShaderResourceViewFromFile(getGraphicsDevice()->getDevice(), L"whitePixel.jpg", NULL, NULL, &whitePixel, NULL);

	D3DX11CreateShaderResourceViewFromFile(getGraphicsDevice()->getDevice(), L"explosion.png", NULL, NULL, &bangTexture, NULL);


	// Compile the pixel shader
	ID3DBlob* pPSBlob = NULL;
	HRESULT hr = RCGame::CompileShaderFromFile(L"SpriteBatch2.fx", "PS", "ps_4_0", &pPSBlob);
	if (FAILED(hr))
	{
		MessageBox(NULL,
			L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK);
		return;
	}

	// Create the pixel shader
	hr = GraphicsDevice.getDevice()->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &pPixelShader);

	spriteBatch = new RCSpriteBatch(getGraphicsDevice());

	D3D11_TEXTURE2D_DESC textureDesc;
	HRESULT result;
	D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
	D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;


	// Initialize the render target texture description.
	ZeroMemory(&textureDesc, sizeof(textureDesc));

	// Setup the render target texture description.
	textureDesc.Width = GraphicsDevice.getViewPort().Width;
	textureDesc.Height = GraphicsDevice.getViewPort().Height;
	textureDesc.MipLevels = 1;
	textureDesc.ArraySize = 1;
	textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	textureDesc.SampleDesc.Count = 1;
	textureDesc.Usage = D3D11_USAGE_DEFAULT;
	textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
	textureDesc.CPUAccessFlags = 0;
	textureDesc.MiscFlags = 0;

	// Create the render target texture.
	result = GraphicsDevice.getDevice()->CreateTexture2D(&textureDesc, NULL, &m_renderTargetTexture);

	// Setup the description of the render target view.
	renderTargetViewDesc.Format = textureDesc.Format;
	renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
	renderTargetViewDesc.Texture2D.MipSlice = 0;

	// Create the render target view.
	result = GraphicsDevice.getDevice()->CreateRenderTargetView(m_renderTargetTexture, &renderTargetViewDesc, &m_renderTargetView);

	// Setup the description of the shader resource view.
	shaderResourceViewDesc.Format = textureDesc.Format;
	shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
	shaderResourceViewDesc.Texture2D.MipLevels = 1;

	// Create the shader resource view.
	result = GraphicsDevice.getDevice()->CreateShaderResourceView(m_renderTargetTexture, &shaderResourceViewDesc, &m_shaderResourceView);


}

void MyGame::Draw(float time, std::vector<IDrawable*>* drawables)
{
	clearColor = XMFLOAT4(1, 0, 0, 1);
	GraphicsDevice.Clear(clearColor, D3D11_CLEAR_DEPTH, 1.0f, 0);

	GraphicsDevice.getDeviceContext()->OMSetRenderTargets(1, &m_renderTargetView, GraphicsDevice.getDepthStencilView());

	RCGame::Draw(time, drawables);

	ID3D11BlendState* bs;
	spriteBatch->getNonPremultiplied(&bs);

	XMVECTOR imgSize = GetTextureSize(mainBG);

	spriteBatch->Begin(Immediate);

	spriteBatch->Draw(mainBG, XMFLOAT4(0 - bg, 0, GraphicsDevice.getViewPort().Width, GraphicsDevice.getViewPort().Height), XMFLOAT2(0, 0), 0, XMFLOAT4(1, 1, 1, 1), 0);
	spriteBatch->Draw(mainBG, XMFLOAT4(GraphicsDevice.getViewPort().Width - bg, 0, GraphicsDevice.getViewPort().Width, GraphicsDevice.getViewPort().Height), XMFLOAT2(0, 0), 0, XMFLOAT4(1, 1, 1, 1), 0);

	spriteBatch->End();


	spriteBatch->Begin(Deferred, bs, nullptr, nullptr, nullptr, nullptr);

	spriteBatch->Draw(layer1, XMFLOAT4(0 - l1, 0, GraphicsDevice.getViewPort().Width, GraphicsDevice.getViewPort().Height), XMFLOAT2(0, 0), 0, XMFLOAT4(1, 1, 1, 1), 0);
	spriteBatch->Draw(layer1, XMFLOAT4(GraphicsDevice.getViewPort().Width - l1, 0, GraphicsDevice.getViewPort().Width, GraphicsDevice.getViewPort().Height), XMFLOAT2(0, 0), 0, XMFLOAT4(1, 1, 1, 1), 0);

	spriteBatch->Draw(layer2, XMFLOAT4(0 - l2, 0, GraphicsDevice.getViewPort().Width, GraphicsDevice.getViewPort().Height), XMFLOAT2(0, 0), 0, XMFLOAT4(1, 1, 1, 1), 0);
	spriteBatch->Draw(layer2, XMFLOAT4(GraphicsDevice.getViewPort().Width - l2, 0, GraphicsDevice.getViewPort().Width, GraphicsDevice.getViewPort().Height), XMFLOAT2(0, 0), 0, XMFLOAT4(1, 1, 1, 1), 0);

	spriteBatch->Draw(pShipTexture, XMFLOAT4(115 * (int)rotation2, 0, 115, 69), XMFLOAT4(playerPos.x, playerPos.y, 115, 69), XMFLOAT2(57.5f, 34.5f), rotation, XMFLOAT4(1, 1, 1, 1), 0);

	RCRectangle shipRect(XMFLOAT4(playerPos.x - 57.5f, playerPos.y - 34.5f, 115, 69));

	for (int m = 0; m<5; m++)
	{
		spriteBatch->Draw(pMineTexture, XMFLOAT4(47 * (int)rotation2, 0, 47, 61), XMFLOAT4(mines[m].x, mines[m].y, 47, 61), XMFLOAT2(23.5, 30.5), 0, XMFLOAT4(1, 1, 1, 1), 0);
		RCRectangle mineRect(XMFLOAT4(mines[m].x - 23.5, mines[m].y - 30.5, 47, 61));

		if (shipRect.Intersects(mineRect))
		{
			spriteBatch->Draw(whitePixel, XMFLOAT4(0, 0, 115, 69), XMFLOAT4(playerPos.x - 57.5f, playerPos.y - 34.5f, 115, 69), XMFLOAT2(0, 0), rotation, XMFLOAT4(1, 0, 0, .25), 0);
			spriteBatch->Draw(whitePixel, XMFLOAT4(0, 0, 47, 61), XMFLOAT4(mines[m].x - 23.5, mines[m].y - 30.5, 47, 61), XMFLOAT2(0, 0), 0, XMFLOAT4(1, 0, 0, .25), 0);

			RCRectangle both = RCRectangle::Union(shipRect, mineRect);
			spriteBatch->Draw(whitePixel, XMFLOAT4(0, 0, 47, 61), XMFLOAT4(both.X, both.Y, both.Width, both.Height), XMFLOAT2(0, 0), 0, XMFLOAT4(0, 0, 1, .25), 0);


			RCRectangle intrsect = RCRectangle::Intersect(shipRect, mineRect);
			spriteBatch->Draw(whitePixel, XMFLOAT4(0, 0, 47, 61), XMFLOAT4(intrsect.X, intrsect.Y, intrsect.Width, intrsect.Height), XMFLOAT2(0, 0), 0, XMFLOAT4(1, 1, 1, .5), 0);
		}

		if (shipRect.Contains(mineRect))
			spriteBatch->Draw(whitePixel, XMFLOAT4(0, 0, 47, 61), XMFLOAT4(mines[m].x - 23.5, mines[m].y - 30.5, 47, 61), XMFLOAT2(0, 0), 0, XMFLOAT4(0, 1, 0, .25), 0);

		mines[m].x -= 3;

		if (mines[m].x < -50)
			mines[m].x = 850;
	}


	int lcnt = 0;
	for (int l = 0; l<4; l++)
	{
		if (!activeLaser[l])
			lcnt++;
	}
	if (lcnt == 4)
	{
		activeLaser[0] = true;
		lasers[0].x = playerPos.x + 57.5;
		lasers[0].y = playerPos.y;
	}

	for (int l = 0; l<4; l++)
	{
		if (activeLaser[l])
		{
			spriteBatch->Draw(laser, XMFLOAT4(lasers[l].x, lasers[l].y, 46, 16), XMFLOAT2(23, 8), 0, XMFLOAT4(1, 1, 1, 1), 0);
			lasers[l].x += 20;

			if (lasers[l].x > GraphicsDevice.getViewPort().Width + 23)
				activeLaser[l] = false;
			else
			{
				shipRect = RCRectangle(XMFLOAT4(lasers[l].x, lasers[l].y, 46, 16));
				for (int m = 0; m<5; m++)
				{
					RCRectangle mineRect(XMFLOAT4(mines[m].x - 23.5, mines[m].y - 30.5, 47, 61));

					if (shipRect.Intersects(mineRect))
					{
						BangAnim* bang = new BangAnim(mines[m]);
						explosion.push_back(std::shared_ptr<BangAnim>(bang));
						mines[m].x = 850;
						activeLaser[l] = false;

						bangSound.Play(L"explosion.wav");

					}

					if (shipRect.Contains(mineRect))
						spriteBatch->Draw(whitePixel, XMFLOAT4(0, 0, 47, 61), XMFLOAT4(mines[m].x - 23.5, mines[m].y - 30.5, 47, 61), XMFLOAT2(0, 0), 0, XMFLOAT4(0, 1, 0, .25), 0);
				}
			}
		}
		else
		{
			lasers[l].x = playerPos.x + 57.5;
			lasers[l].y = playerPos.y;

			int nextl = l - 1;
			if (l == 0)
				nextl = 3;

			float dist = sqrt((lasers[nextl].x - lasers[l].x) + (lasers[nextl].y - lasers[l].y));

			if (dist > sqrt(155))
			{
				laserSound.Play(L"sound01.wav");
				activeLaser[l] = true;
			}
		}
	}
	// Exposions
	if (explosion.size() > 0)
	{
		std::list<std::shared_ptr<BangAnim>> droplist;
		for (auto e = explosion.rbegin(); e != explosion.rend(); e++)
		{
			if ((*e)->frame < 12)
			{
				spriteBatch->Draw(bangTexture, XMFLOAT4(133.5 * (int)(*e)->frame, 0, 133.5, 134), XMFLOAT4((*e)->position.x, (*e)->position.y, 64, 64), XMFLOAT2(30.5, 30.5), 0, XMFLOAT4(1, 1, 1, 1), 0);
				(*e)->frame += .5f;
			}
			else
				droplist.push_back((*e));
		}
		if (droplist.size() > 0)
		{
			for (auto e = droplist.begin(); e != droplist.end(); e++)
				explosion.remove((*e));

			droplist.clear();
		}
	}
	spriteBatch->End();
	
	rotation2 += .5f;

	bg += .5f;
	l1 += 1;
	l2 += 2;

	if (bg > GraphicsDevice.getViewPort().Width)
		bg = 0;

	if (l1 > GraphicsDevice.getViewPort().Width)
		l1 = 0;

	if (l2 > GraphicsDevice.getViewPort().Width)
		l2 = 0;

	if (rotation2 > 8)
		rotation2 = 0;


	//http://www.rastertek.com/dx11tut22.html

	ID3D11RenderTargetView* rtv = GraphicsDevice.getRenderTerget();
	GraphicsDevice.getDeviceContext()->OMSetRenderTargets(1, &rtv, GraphicsDevice.getDepthStencilView());

	ID3D11BlendState* opaque;
	spriteBatch->getOpaque(&opaque);

	spriteBatch->Begin(Deferred, opaque, nullptr, nullptr, nullptr, [&](){SetPS(); });

	float width = GraphicsDevice.getViewPort().Width / 2;
	float height = GraphicsDevice.getViewPort().Height / 2;

	spriteBatch->Draw(m_shaderResourceView, XMFLOAT4(0, 0, 800, 480), XMFLOAT2(0, 0), 0, XMFLOAT4(1, 1, 1, 1), 0);

	spriteBatch->End();

}

void MyGame::SetPS()
{
	GraphicsDevice.getDeviceContext()->PSSetShader(pPixelShader, NULL, 0);
}